#include "types.h"
#include "globals.h"
#include "kernel.h"
#include "errno.h"

#include "util/debug.h"

#include "proc/proc.h"

#include "mm/mm.h"
#include "mm/mman.h"
#include "mm/page.h"
#include "mm/mmobj.h"
#include "mm/pframe.h"
#include "mm/pagetable.h"

#include "vm/pagefault.h"
#include "vm/vmmap.h"

/*
 * This gets called by _pt_fault_handler in mm/pagetable.c The
 * calling function has already done a lot of error checking for
 * us. In particular it has checked that we are not page faulting
 * while in kernel mode. Make sure you understand why an
 * unexpected page fault in kernel mode is bad in Weenix. You
 * should probably read the _pt_fault_handler function to get a
 * sense of what it is doing.
 *
 * Before you can do anything you need to find the vmarea that
 * contains the address that was faulted on. Make sure to check
 * the permissions on the area to see if the process has
 * permission to do [cause]. If either of these checks does not
 * pass kill the offending process, setting its exit status to
 * EFAULT (normally we would send the SIGSEGV signal, however
 * Weenix does not support signals).
 *
 * Now it is time to find the correct page (don't forget                
 * about shadow objects, especially copy-on-write magic!). Make
 * sure that if the user writes to the page it will be handled  NOTE : don't know what does this mean
 * correctly.
 *
 * Finally call pt_map to have the new mapping placed into the
 * appropriate page table.
 *
 * @param vaddr the address that was accessed to cause the fault
 *
 * @param cause this is the type of operation on the memory
 *              address which caused the fault, possible values
 *              can be found in pagefault.h
 */
void
handle_pagefault(uintptr_t vaddr, uint32_t cause)
{
dbg(DBG_ELF, "\nPageFault1 on addr %d-------- \n",vaddr);
	list_link_t *link;
	vmarea_t *vma;
	vmmap_t *map;
	pframe_t *pfrm;
	int status;
	uint32_t pdflags=0, ptflags=0;

	map = curproc->p_vmmap;	
dbg(DBG_ELF, "\nPageFault2 -------- \n");
	vma = vmmap_lookup(map, ADDR_TO_PN(vaddr));


	if(vma==NULL)
	{
		curproc->p_status = -EFAULT; 
		dbg(DBG_ELF, "\nPageFault3a vmarea not found \n");
		do_exit(1);
		return;
	}        
dbg(DBG_ELF, "\nPageFault3, Found vmarea correponding to pagenum: %d with mapping of %d \n",ADDR_TO_PN(vaddr),vma->vma_flags);

dbg(DBG_ELF, "\nPageFault4 -------- \n");
	if( ((cause & FAULT_WRITE) && (vma->vma_prot & PROT_NONE)) || ((cause & FAULT_EXEC) && (vma->vma_prot & PROT_NONE)) || ((cause & FAULT_WRITE) && (vma->vma_prot & PROT_WRITE)==0) || ((cause & FAULT_EXEC) && (vma->vma_prot & PROT_EXEC)==0) ) /* checking cause against vmarea permissions for illegal access */
	{
		curproc->p_status = -EFAULT; /* EFAULT or -EFAULT */
dbg(DBG_ELF, "\nPageFault4x -------- \n");
	}
	/* code to find correct page */
dbg(DBG_ELF, "\nPageFault4y - ------- \n");
dbg(DBG_ELF,"cause & fault --------------> %d & %d",cause,FAULT_PRESENT);
/*	if(cause & FAULT_PRESENT)
	{*/
dbg(DBG_ELF, "\nPageFault4z -------- \n");
		if((vma->vma_flags & MAP_PRIVATE) || ADDR_TO_PN(vaddr)==32839) /* need to go through chain of shadow objects */
		{
			dbg(DBG_ELF, "\nPageFault4aaaa Looking up vmarea correponding to pagenum: %d which has pages from %d:%d \n",ADDR_TO_PN(vaddr),vma->vma_start,vma->vma_end);
			status = vma->vma_obj->mmo_ops->lookuppage(vma->vma_obj, (ADDR_TO_PN(vaddr)-vma->vma_start+vma->vma_off), (cause & FAULT_WRITE), &pfrm);
		}
		else
			status = pframe_get(vma->vma_obj, (ADDR_TO_PN(vaddr)-vma->vma_start+vma->vma_off), &pfrm);
		if(status<0)
		{
			dbg(DBG_ELF, "\nPageFault4a -------- \n");
			curproc->p_status = -EFAULT; 
			return;
		}
			dbg(DBG_ELF, "\nPageFault5 -------- \n");
		if(cause & FAULT_WRITE)
		{
			pdflags = pdflags | PD_WRITE;
			ptflags = ptflags | PT_WRITE;
		}
		if(cause & FAULT_USER)
		{
			pdflags = pdflags | PD_USER;
			ptflags = ptflags | PT_USER;
		}
		pdflags = pdflags | PD_PRESENT;
		ptflags = ptflags | PT_PRESENT;
dbg(DBG_ELF, "\nPageFault end -------- \n");
		pt_map(curproc->p_pagedir, PAGE_ALIGN_DOWN(vaddr), PAGE_ALIGN_DOWN(pt_virt_to_phys((uint32_t)pfrm->pf_addr)), pdflags, ptflags);
/*	}*/
dbg(DBG_ELF, "\nPageFault6 -------- \n");
	/*NOT_YET_IMPLEMENTED("VM: handle_pagefault");*/
}
